package monitor

import (
	"bytes"
	"fmt"
	"image/png"
	"net"
	"net/http"
	"strconv"
	"time"

	"github.com/juju/ratelimit"
	"github.com/smallnest/log"
	"github.com/songtianyi/wechat-go/plugins/wxweb/config"
	"github.com/songtianyi/wechat-go/plugins/wxweb/system"
	"github.com/songtianyi/wechat-go/wxweb"
)

type Dashboard struct {
	ID        int
	Name      string
	URL       string
	Dashboard string
	Tag       string
	Panels    int
	OrgID     int
	Type      string
}

type Server struct {
	Rate             *ratelimit.Bucket
	Group            bool
	Session          *wxweb.Session
	httpClient       *http.Client
	registerUserName string
	dashboards       map[int]Dashboard
}

func NewServer(rt time.Duration, group bool, dashboards map[int]Dashboard) *Server {

	netTransport := &http.Transport{
		Dial: (&net.Dialer{
			Timeout: 5 * time.Second,
		}).Dial,
		TLSHandshakeTimeout: 5 * time.Second,
		MaxIdleConns:        200,
		MaxIdleConnsPerHost: 200,
	}

	httpClient := &http.Client{
		Timeout:   5 * time.Second,
		Transport: netTransport,
	}

	return &Server{
		Rate:       ratelimit.NewBucketWithQuantum(rt, 1, 1),
		Group:      group,
		httpClient: httpClient,
		dashboards: dashboards,
	}
}
func (s *Server) Serve() (err error) {
	defer func() {
		if err := recover(); err != nil {
			log.Infof("panic: %v", err)
		}
	}()

	s.Session, err = wxweb.CreateSession(nil, nil, wxweb.TERMINAL_MODE)
	if err != nil {
		log.Error(err)
		return
	}
	// load plugins for this session
	s.registerMonitor()
	system.Register(s.Session)
	config.Register(s.Session)

	if err = s.Session.HandlerRegister.EnableByType(wxweb.MSG_SYS); err != nil {
		log.Error(err)
		return
	}

	for {
		if err := s.Session.LoginAndServe(false); err != nil {
			log.Error("session exit, %s", err)
			for i := 0; i < 3; i++ {
				log.Info("trying re-login with cache")
				if err := s.Session.LoginAndServe(true); err != nil {
					log.Error("re-login error or session down, %s", err)
				}
				time.Sleep(3 * time.Second)
			}
			if s.Session, err = wxweb.CreateSession(nil, s.Session.HandlerRegister, wxweb.TERMINAL_MODE); err != nil {
				log.Error("create new sesion failed, %s", err)
				break
			}
		} else {
			log.Info("closed by user")
			break
		}
	}

	return nil
}

func (s *Server) registerMonitor() {
	s.Session.HandlerRegister.Add(wxweb.MSG_TEXT, wxweb.Handler(s.autoReplyMonitor), "monitor")
	if err := s.Session.HandlerRegister.EnableByName("monitor"); err != nil {
		log.Error(err)
	}
}

func help(dashboards map[int]Dashboard) string {
	sb := bytes.NewBufferString("帮助信息\n")
	for i := 0; i <= 25; i++ {
		d, ok := dashboards[i]
		if ok {
			sb.WriteString(fmt.Sprintf("%d: %s \n", i, d.Name))
		}
	}
	return sb.String()
}

func (s *Server) autoReplyMonitor(session *wxweb.Session, msg *wxweb.ReceivedMessage) {
	// 支持群聊 或者群聊/单聊
	isGroup := msg.IsGroup || !s.Group

	if !isGroup {
		return
	}

	if isGroup {
		switch msg.Content {
		case "register":
			s.registerUserName = msg.ToUserName
			s.Session.SendText("注册成功，注意微信对命令的响应有10秒左右的延迟", s.Session.Bot.UserName, s.registerUserName)
			return
		case "unregister":
			s.registerUserName = ""
			s.Session.SendText("反注册成功", s.Session.Bot.UserName, s.registerUserName)
			return
		case "help":
			s.Session.SendText(help(s.dashboards), s.Session.Bot.UserName, s.registerUserName)
			return
		}

	}

	if isGroup && (msg.ToUserName == s.registerUserName || msg.FromUserName == s.registerUserName) {
		token := s.Rate.TakeAvailable(1)
		if token == 0 {
			s.Session.SendText("命令下发太快，运维小秘书还在准备中，请稍后再试", s.Session.Bot.UserName, s.registerUserName)
			return
		}

		cmd := msg.Content
		if k, err := strconv.Atoi(cmd); err == nil {
			if d, ok := s.dashboards[k]; ok {
				if d.Type == "grafana" {
					hours := 1
					if img := grafanaDashboard(s.httpClient, d.URL+d.Dashboard, d.Panels, d.OrgID, hours); img != nil {
						tmpfile, _ := TempFile("", "wb_monitor", ".png")
						png.Encode(tmpfile, img)

						s.Session.SendImg(tmpfile.Name(), s.Session.Bot.UserName, s.registerUserName)
					}
				} else if d.Type == "graphite" {
					if img := graphiteDashboard(s.httpClient, d.Tag, d.URL); img != nil {
						tmpfile, _ := TempFile("", "wb_monitor", ".png")
						png.Encode(tmpfile, img)

						s.Session.SendImg(tmpfile.Name(), s.Session.Bot.UserName, s.registerUserName)
					}
				}
			}
		}
	}
}
